// *****************************************************************************
// CHANGE LOG
//
// *****************************************************************************

#include <p32xxxx.h>
#include <plib.h>
#include <string>
#include <vector>
#include <sstream>
#include "SerialPort.h"

// Have to declare static members so they are aviliable
std::vector<struct SerialPort::SerialPortBuffer *> SerialPort::_buffers;

/*! \brief Initializes a new instance of the Uart class with the specified port.
 *
 * Opens the specified port.
 *
 * Values:\n
 * Baud Rate: 9600\n
 * Data Bits: 8\n
 * Parity: None\n
 * Stop Bits: 1\n
 * Flow Control: None
 */
SerialPort::SerialPort(const UART_MODULE portId)
{
	this->Initialize(portId, 9600);
}

/*! \brief Initializes a new instance of the Uart class with the specified port
 * and baud rate.
 *
 * Opens the specified port using the following defaults:\n
 *
 * Values: \n
 * Baud Rate: \b baudRate \n
 * Data Bits: 8\n
 * Parity: None\n
 * Stop Bits: 1\n
 * Flow Control: None
 */
SerialPort::SerialPort(const UART_MODULE portId, const UINT32 baudRate)
{
	this->Initialize(portId, baudRate);
}

/*! \brief Initializes a new instance of the Uart class with the specified port
 * and baud rate.
 *
 * Opens the specified port using the following defaults:
 *
 * Values: \n
 * Baud Rate: \b baudRate \n
 * Data Bits: 8\n
 * Parity: None\n
 * Stop Bits: 1\n
 * Flow Control: None
 */
void SerialPort::Initialize(const UART_MODULE portId, const UINT32 baudRate)
{

	this->_portId = portId;
	this->_baudRate = baudRate;

	this->_invertPolarity = false;

	// Configure port based on polarity
	if (this->_invertPolarity)
		UARTConfigure(this->_portId, (UART_CONFIGURATION) (UART_INVERT_RECEIVE_POLARITY | UART_ENABLE_PINS_TX_RX_ONLY | UART_ENABLE_HIGH_SPEED | UART_INVERT_TRANSMIT_POLARITY));
	else
		UARTConfigure(this->_portId, (UART_CONFIGURATION) (UART_ENABLE_PINS_TX_RX_ONLY | UART_ENABLE_HIGH_SPEED));

	// Setup RX Interrupt
	UARTSetFifoMode(this->_portId, (UART_FIFO_MODE) (UART_INTERRUPT_ON_RX_NOT_EMPTY));

	// Set Interrupt priorities
#ifdef __32MX795F512H__
	INTSetVectorPriority(INT_UART_5_VECTOR, INT_PRIORITY_LEVEL_2);
	INTSetVectorSubPriority(INT_UART_5_VECTOR, INT_SUB_PRIORITY_LEVEL_0);
	INTSetVectorPriority(INT_UART_2_VECTOR, INT_PRIORITY_LEVEL_2);
	INTSetVectorSubPriority(INT_UART_2_VECTOR, INT_SUB_PRIORITY_LEVEL_0);
#else
	INTSetVectorPriority(INT_UART_2_VECTOR, INT_PRIORITY_LEVEL_2);
	INTSetVectorSubPriority(INT_UART_2_VECTOR, INT_SUB_PRIORITY_LEVEL_0);
#endif
	// Enable RX Interrupt
	//INTEnable((INT_SOURCE) (INT_U1RX + portId), INT_ENABLED);
	INTEnable(INT_U5RX, INT_ENABLED);
	INTEnable(INT_U2RX, INT_ENABLED);

	// Open Serial Port
	this->Open();

	// If one of the SPI channels had an output on a UART TX Pin,
	// the receiver will have gotten bad characters and may still be
	// waiting for data.

	// Wait 10ms
	WriteCoreTimer(0);
	while (ReadCoreTimer() < 4E5) { Nop(); }

	// Configure buffer and register it with static buffer vector
	this->_buffer.portId = portId;
	this->_buffer.buffer = "";
	this->_buffer.messageReady = false;
	SerialPort::_buffers.push_back(&this->_buffer);
}

/*! \brief Closes Uart interface and releases all resources.
 *
 * This destructor closes all associated resources for this UART instance
 * including disabling interrupts and sets the IO pins to their default
 * configuration.
 */
SerialPort::~SerialPort()
{
	// Disable Interrupt Priority
	INTSetVectorPriority(INT_UART_2_VECTOR, INT_PRIORITY_DISABLED);

	// Disable RX Interrupt
	INTEnable(INT_U2RX, INT_DISABLED);

	// Disable Serial Port
	UARTEnable(this->_portId, (UART_ENABLE_MODE) (UART_DISABLE));

	// Deregister buffer from static vector
	for (INT32 i = 0; i < SerialPort::_buffers.size(); i++)
	{
		if (SerialPort::_buffers[i]->portId != this->_portId)
			continue;

		SerialPort::_buffers.erase(SerialPort::_buffers.begin() + i);
		break;
	}
}

/*! \brief Writes a single byte.
 */
void SerialPort::Write(const BYTE data)
{
	UARTSendDataByte(this->_portId, data);
	while (!UARTTransmitterIsReady(this->_portId));
}

/*! \brief Writes multiple bytes.
 */
void SerialPort::Write(const BYTE *buffer, const UINT32 index, const UINT32 length)
{
	for (INT32 i = index; i < i + length; i++)
		this->Write(buffer[i]);
}

/*! \brief Writes string.
 */
void SerialPort::Write(const std::string data)
{
	for (INT32 i = 0; i < data.length(); i++)
		this->Write(data[i]);
}

/*! \brief Opens the UART port.
 */
void SerialPort::Open()
{
	UARTSetDataRate(this->_portId, PB_CLK, this->_baudRate);
	UARTEnable(this->_portId, (UART_ENABLE_MODE) (UART_ENABLE | UART_PERIPHERAL | UART_RX | UART_TX));
}

/*! \brief Closes the UART port.
 */
void SerialPort::Close()
{
	UARTEnable(this->_portId, (UART_ENABLE_MODE) (UART_DISABLE | UART_PERIPHERAL | UART_RX | UART_TX));
}

/*! \brief Sets the baud rate.
 *
 * @param baudRate Baud rate to set UART to
 */
void SerialPort::SetBaudRate(const UINT32 baudRate)
{
	UARTSetDataRate(this->_portId, PB_CLK, _baudRate);
}

/*! \brief Appends a single character to the Uart buffer
 *
 * @param portId The port id of the buffer
 * @param data Data to append to the buffer
 */
void SerialPort::AppendBuffer(const UART_MODULE portId, const BYTE data)
{
	struct SerialPortBuffer *buffer = SerialPort::GetBuffer(portId);
	if (buffer == NULL)
		return;

	// Check for EOT
	if (data == (BYTE) SerialPort::Delimiters::Eot)
		buffer->messageReady = true;

	buffer->buffer += data;
}

/*! \brief Reads the Uart buffer
 *
 * @param portId The port id of the buffer
 */
std::string SerialPort::ReadBuffer(const UART_MODULE portId)
{
	struct SerialPortBuffer *buffer = SerialPort::GetBuffer(portId);
	if (buffer == NULL)
		return "";

	return buffer->buffer;
}

/*! \brief Clears the Uart buffer
 *
 * @param portId The port id of the buffer
 */
void SerialPort::ClearBuffer(const UART_MODULE portId)
{
	struct SerialPortBuffer *buffer = SerialPort::GetBuffer(portId);
	if (buffer == NULL)
		return;

	buffer->buffer.clear();
	buffer->messageReady = false;
}

/*! \brief Clears the Uart buffer
 *
 */
void SerialPort::ClearBuffer()
{
	this->_buffer.buffer.clear();
	this->_buffer.messageReady = false;
}

/*! \brief Returns a reference to the Uart buffer
 *
 * @param portId The port id of the buffer
 */
struct SerialPort::SerialPortBuffer* SerialPort::GetBuffer(const UART_MODULE portId)
{
	for (INT32 i = 0; i < SerialPort::_buffers.size(); i++)
	{
		if (SerialPort::_buffers[i]->portId != portId)
			continue;

		return SerialPort::_buffers[i];
	}

	return NULL;
}

bool SerialPort::GetInvertedPolarity()
{
	return this->_invertPolarity;
}

struct SerialPort::SerialPortBuffer* SerialPort::GetBuffer()
{
	return &this->_buffer;
}

extern "C"
void _mon_putc(char ch)
{
	// Send Byte
	UARTSendDataByte((UART_MODULE)(__XC_UART - 1), ch);

	// Wait until the byte is finished
	while (!UARTTransmitterIsReady((UART_MODULE)(__XC_UART - 1))) {Nop();}
}

/*! \brief UART1 RX Interrupt
 */
extern "C"
void __ISR(_UART_1_VECTOR, ipl2) SerialPort1_OnDataReceived(void)
{
	// clear the interrupt flag
	INTClearFlag(INT_U1RX);

	while (UARTReceivedDataIsAvailable(UART1))
	{
		SerialPort::AppendBuffer(UART1, UARTGetDataByte(UART1));
	}
}

/*! \brief UART2 RX Interrupt
 */
extern "C"
void __ISR(_UART_2_VECTOR, ipl2) SerialPort2_OnDataReceived(void)
{
	// clear the interrupt flag
	INTClearFlag(INT_U2RX);

	while (UARTReceivedDataIsAvailable(UART2))
	{
		SerialPort::AppendBuffer(UART2, UARTGetDataByte(UART2));
	}
}

/*! \brief UART2 RX Interrupt
 */
extern "C"
void __ISR(_UART_3_VECTOR, ipl2) SerialPort3_OnDataReceived(void)
{
	// clear the interrupt flag
	INTClearFlag(INT_U3RX);

	while (UARTReceivedDataIsAvailable(UART3))
	{
		SerialPort::AppendBuffer(UART3, UARTGetDataByte(UART3));
	}
}

/*! \brief UART2 RX Interrupt
 */
extern "C"
void __ISR(_UART_4_VECTOR, ipl2) SerialPort4_OnDataReceived(void)
{
	// clear the interrupt flag
	INTClearFlag(INT_U4RX);

	while (UARTReceivedDataIsAvailable(UART4))
	{
		SerialPort::AppendBuffer(UART4, UARTGetDataByte(UART4));
	}
}

/*! \brief UART2 RX Interrupt
 */
extern "C"
void __ISR(_UART_5_VECTOR, ipl2) SerialPort5_OnDataReceived(void)
{
	// clear the interrupt flag
	INTClearFlag(INT_U5RX);

	while (UARTReceivedDataIsAvailable(UART5))
	{
		SerialPort::AppendBuffer(UART5, UARTGetDataByte(UART5));
	}
}

/*! \brief UART2 RX Interrupt
 */
extern "C"
void __ISR(_UART_6_VECTOR, ipl2) SerialPort6_OnDataReceived(void)
{
	// clear the interrupt flag
	INTClearFlag(INT_U6RX);

	while (UARTReceivedDataIsAvailable(UART6))
	{
		SerialPort::AppendBuffer(UART6, UARTGetDataByte(UART6));
	}
}
